package com.jbm.core.common;

import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * Created by Sun on 2016/11/12.
 */

class CycPosition
{
    public CycPosition()
    {
        this.index = 0;
        this.offset = 0;
        this.buffer = null;
        this.totaloffset = 0;
    }

    protected int index;
    protected int offset;
    protected int totaloffset;
    protected byte[] buffer;

    public CycPosition copy ()
    {
        CycPosition cycp = new CycPosition();

        cycp.index = index;
        cycp.offset = offset;
        cycp.buffer = buffer;
        cycp.totaloffset = totaloffset;

        return cycp;
    }

    public boolean next (ArrayList list, boolean marked )
    {
        if ( index >= list.size() )
            return false;

        offset = 0;
        if ( marked )
            index ++;
        else
        {
            CycBuffer.totalpa --;
            list.remove(index);
            totaloffset -= buffer.length;
        }

        if ( index < list.size() )
            buffer = (byte[])list.get(index);
        else
        {
            buffer = null;
            return false;
        }

        return true;
    }

    public void move ( CycPosition tar, ArrayList list )
    {
        for ( int i = index; i < tar.index; i ++ )
        {
            buffer = (byte[])list.get(index);
            CycBuffer.totalpa --;
            list.remove(index);
            tar.totaloffset -= buffer.length;
        }

        tar.index = index;
    }

    public boolean reload ( ArrayList list )
    {
        if ( index < list.size() )
            buffer = (byte[])list.get(index);
        else
            return false;

        return true;
    }
}

public class CycBuffer
{
    public CycBuffer()
    {
        buflist = new ArrayList();
        mark = null;
        position = new CycPosition();
        size = 0;
        this.blockqueue = new LinkedBlockingDeque(1);
    }

    public int get( byte[] buffer )
    {
        int blen = 0;

        synchronized (this)
        {
            position.reload(buflist);

            while (position.index < buflist.size() && blen < buffer.length)
            {
                int copylen = buffer.length - blen > position.buffer.length - position.offset ?
                        position.buffer.length - position.offset : buffer.length - blen;

                System.arraycopy(position.buffer, position.offset, buffer, blen, copylen);
                blen += copylen;
                position.offset += copylen;

                if (position.buffer.length == position.offset)
                    position.next(buflist, mark != null);
            }

            position.totaloffset += blen;
            size -= blen;
        }

        return blen;
    }

    public int get( byte[] buffer, int offset, int length )
    {
        length = buffer.length - offset > length ? length : buffer.length - offset;

        int blen = 0;

        synchronized (this)
        {
            position.reload(buflist);

            while (position.index < buflist.size() && blen < length)
            {
                int copylen = length - blen > position.buffer.length - position.offset ?
                        position.buffer.length - position.offset : length - blen;

                System.arraycopy(position.buffer, position.offset, buffer, blen+offset, copylen);
                blen += copylen;
                position.offset += copylen;

                if (position.buffer.length == position.offset)
                    position.next(buflist, mark != null);
            }

            position.totaloffset += blen;
            size -= blen;
        }

        return blen;
    }

    public int skip ( int len )
    {
        int blen = 0;

        synchronized (this)
        {
            position.reload(buflist);

            while (position.index < buflist.size() && blen < len)
            {
                int copylen = len - blen > position.buffer.length - position.offset ?
                        position.buffer.length - position.offset : len - blen;

                blen += copylen;
                position.offset += copylen;

                if (position.buffer.length == position.offset)
                    position.next(buflist, mark != null);
            }

            position.totaloffset += blen;
            size -= blen;
        }

        return blen;
    }

    public void mark ()
    {
        synchronized (this)
        {
            if ( mark != null )
                unmark();
            mark = position.copy();
        }
    }

    public boolean reset()
    {
        synchronized (this)
        {
            if (mark == null)
                return false;

            size += position.totaloffset - mark.totaloffset;
            position = mark.copy();
            mark = null;
        }

        return true;
    }

    public void unmark()
    {
        synchronized (this)
        {
            if (mark == null)
                return;

            mark.move(position, buflist);
            mark = null;
        }
    }

    public void put ( byte[] buffer )
    {
        synchronized (this)
        {
            totalpa ++;
            size += buffer.length;
            buflist.add(buffer);
            blockqueue.offer("1");
//            SIPMediaHandler.logger.debug("cycbuffer.totalpa = "+totalpa+" size = "+size+" list.count = "+buflist.size());
        }
    }

    public int remaining ()
    {
        synchronized (this)
        {
            return size;
        }
    }

    public void waitinput()
    {
        try
        {
            blockqueue.take();
        }catch (Exception e){}
    }

    private int size;

    protected static int totalpa = 0;

    private ArrayList buflist;

    private CycPosition mark;
    private CycPosition position;
    protected BlockingQueue blockqueue;
}
